/*
 * file: FTP.java
 */

/**
 *
 * @author Christina Ng Gek Ling U081583X
 * @author Novita Mayasari U085510R
 */
import java.io.*;
import java.net.*;

public class FTP {

    /**
     * Socket:
     * variables declaration to establish connection between client
     * and FTP server
     */
    private Socket ftpSocket = null;
    //PrintStream: send info to FTP server
    private PrintStream serverSender = null;
    //BufferedReader: read info from FTP server
    private BufferedReader serverReader = null;

    public FTP() {
    }

    /**
     * connect: establish connection between client and FTP server
     * @param host The server IP address
     * @param port The server port number
     * @return
     * @throws UnknownHostException
     * @throws IOException
     */
    public String connect(String host, int port) throws UnknownHostException, IOException {
        //initiate connection to FTP server
        ftpSocket = new Socket(host, port);

        //initiate PrintStream to send info from client to FTP server
        serverSender = new PrintStream(ftpSocket.getOutputStream());

        //initiate BufferedReader to receve info from FTP server
        serverReader = new BufferedReader(new InputStreamReader(ftpSocket.getInputStream()));

        return getResponseMessage().toString();
    }

    /**
     * login: login to FTP server
     * @param username
     * @param password
     * @return
     * @throws IOException
     */
    public String login(String username, String password) throws IOException {

        //send command "user" + specified username to FTP server
        serverSender.println("user " + username);

        StringBuffer response = new StringBuffer();
        response.append(getResponseMessage().toString() + "\n");

        //send command "password " follow by the password specified to the FTP server
        serverSender.println("pass " + password);
        response.append(getResponseMessage().toString());

        return response.toString();
    }

    /**
     * disconnect: disconnect from FTP server
     * @return
     * @throws IOException
     */
    public String disconnect() throws IOException {
        String response = null;

        if (serverSender != null) {
            serverSender.println("quit");
            response = getResponseMessage().toString();
            serverSender.close();
        }
        if (serverReader != null) {
            serverReader.close();
        }
        if (ftpSocket != null) {
            ftpSocket.close();
        }

        return response;
    }

    /**
     * changeDir: change current working directory
     * @param dir
     * @return
     * @throws IOException
     */
    public String changeDir(String dir) throws IOException {
        //send command "cwd " follow by the directory specified to FTP server
        serverSender.println("cwd " + dir);
        return getResponseMessage().toString();
    }

    /**
     * getCurrDir: return to current working directory
     * @return
     * @throws IOException
     */
    public String getCurrDir() throws IOException {
        //send command "pwd " to the FTP server
        serverSender.println("pwd ");
        return getResponseMessage().toString();
    }

    /**
     * backToParentDirectory: return to the parent directory
     * @return
     * @throws IOException
     */
    public String backToParentDirectory() throws IOException
    {
    	//send command "cdup " to the FTP server
        serverSender.println("cdup ");

        //return the response message to the user
        return getResponseMessage().toString();
    }

    /**
     * deleteFile method: delete the file specified
     * @param filename
     * @return
     * @throws IOException
     */
    public String deleteFile(String filename) throws IOException
    {
    	//send command "dele " follow by the filename specified to the FTP server
        serverSender.println("dele " + filename);

        //return the response message to the user
        return getResponseMessage().toString();
    }



    /**
     * To filter response code from the response message
     * @return
     * @throws IOException
     */
    public int getResponseCode() throws IOException {
        //filter the response code from the response message
        int responseCode = Integer.parseInt(serverReader.readLine().substring(0, 3));
        return responseCode;
    }

    /**
     * To get the entire response message
     * @return
     * @throws IOException
     */
    public StringBuffer getResponseMessage() throws IOException {
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
        } finally {
            StringBuffer returnString = new StringBuffer();
            String s = null;
            do {
                s = serverReader.readLine();
                returnString.append(s + "\n");
            } while (serverReader.ready() == true);
            return returnString;
        }
    }

    private PrintStream getServerSender() {
        return serverSender;
    }

    private BufferedReader getServerReader() {
        return serverReader;
    }

    /**
     * listFiles: to return all files in the specified directory
     * @param params
     * @return
     * @throws IOException
     */
    @SuppressWarnings("empty-statement")
    public String listFiles(String params) throws IOException {

        StringBuffer files = new StringBuffer();
        System.out.println("files created");

        // EPSV MODE
        //send command "epsv " to the FTP server to get the extended passive mode address
        serverSender.println("epsv");
        System.out.println("entering epsv mode");
        String host = getResponseMessage().toString();

        //extracts port number & IP address
        int portNumber = Integer.parseInt(getOnlyNumerics(host));
        InetAddress add = ftpSocket.getInetAddress();
        System.out.println("IP address from getInetAdd: " + add); // Can remove this line
        System.out.println("Port number: " + portNumber);         // Can remove this line

        serverSender.println("list");

        //creates the data socket connection for data transfer
        Socket clientSocket = new Socket(add, portNumber);
        InputStream in = clientSocket.getInputStream();

        //creates the buffer for the transfer
        byte buffer[] = new byte[4096];
        int offset = 0;

        //read data from the input stream and write data to the output stream
        while ((offset = in.read(buffer)) >= 0) {
            files.append(new String(buffer, 0, offset));
        }

        files.append(getResponseMessage());
        in.close();
        clientSocket.close();

        return files.toString();
    }

    /**
     * getFile: return response message (status of the download)
     * @param ftpPath
     * @param localPath
     * @return
     * @throws IOException
     */
    public String getFile(String ftpPath, String localPath) throws IOException {
        File file = new File(localPath);
        FileOutputStream fileOutputStream = new FileOutputStream(file);

        //send command "epsv " to the FTP server to get the extended passive mode address
        serverSender.println("epsv");

        String host = getResponseMessage().toString();

        if (!host.startsWith("229")) {
            fileOutputStream.flush();
            fileOutputStream.close();
            return host;
        }

        //extracts port number & IP address
        int portNumber = Integer.parseInt(getOnlyNumerics(host));
        InetAddress add = ftpSocket.getInetAddress();
        System.out.println("IP address from getInetAdd: " + add);
        System.out.println("Port number: " + portNumber);


        //send command "retr" to the FTP server to get the specified file
        serverSender.println("retr " + ftpPath);

        //creates the data socket connection for data transfer
        Socket clientSocket = new Socket(add, portNumber);
        InputStream in = clientSocket.getInputStream();

        //creates the buffer for the transfer
        byte buffer[] = new byte[4096];
        int offset = 0;

        //read data from the input stream and write data to the output stream
        while ((offset = in.read(buffer)) > 0) {
            fileOutputStream.write(buffer, 0, offset);
        }

        //flush and close the output stream
        fileOutputStream.flush();
        fileOutputStream.close();
        //close the input stream
        in.close();
        //close the data socket connection
        clientSocket.close();

        //return the response message to the user
        return getResponseMessage().toString();
    }

    /**
     * storeFile: return response message (status of the upload)
     * @param ftpPath
     * @param localPath
     * @return
     * @throws IOException
     */
    public String storeFile(String ftpPath, String localPath) throws IOException {
        File file = new File(localPath);
        FileInputStream fileInputStream = new FileInputStream(file);

        //send command "epsv " to the FTP server to get the extended passive mode address
        serverSender.println("epsv");

        //get the responseMessage which contains the passive mode address
        String host = getResponseMessage().toString();

        if (!host.startsWith("229")) {
            fileInputStream.close();
            return host;
        }

        //extracts port number & IP address
        int portNumber = Integer.parseInt(getOnlyNumerics(host));
        InetAddress add = ftpSocket.getInetAddress();
        System.out.println("IP address from getInetAdd: " + add);
        System.out.println("Port number: " + portNumber);

        //send command "stor" to the FTP server to get the specified file
        serverSender.println("stor " + ftpPath);

        //creates the data socket connection for data transfer
        Socket clientSocket = new Socket(add, portNumber);
        OutputStream outputStream = clientSocket.getOutputStream();

        //creates the buffer for the transfer
        byte buffer[] = new byte[4096];
        int offset = 0;

        //read data from the input stream and write data to the output stream
        while ((offset = fileInputStream.read(buffer)) > 0) {
            outputStream.write(buffer, 0, offset);
        }

        //flush and close the output stream
        outputStream.flush();
        outputStream.close();
        //close the input stream
        fileInputStream.close();
        //close the data socket connection
        clientSocket.close();

        //return the response message to the user
        return getResponseMessage().toString();
    }

    /**
     * Extract the integers value from a string
     * Supporting method used to get the port number for epsv mode
     * @param str
     * @return
     */
    private static String getOnlyNumerics(String str) {

        if (str == null) {
            return null;
        }
        StringBuffer strBuff = new StringBuffer();
        char c;
        for (int i = 3; i < str.length(); i++) {
            c = str.charAt(i);
            if (Character.isDigit(c)) {
                strBuff.append(c);
            }
        }
        return strBuff.toString();
    }
}
